<?php

class ToolkitFile
{
  private function __construct() {}
  
  public static function getHashPathForLevel($name, $levels, $generate = true)
  {
    if ($levels == 0)
    {
      return '';
    }
    else
    {
      $hash = $generate ? md5($name) : $name;
      $path = '/';
      for ($i = 1; $i <= $levels; $i++)
      {
        $path .= substr( $hash, 0, $i ) . '/';
      }
      return rtrim($path, '/');
    }
  }
  public static function sha1Base36($path)
  {
    $hash = sha1_file($path);
    return ($hash === false) ? false : self::baseConvert($hash, 16, 36, 31);
  }
  /**
   * Convert an arbitrarily-long digit string from one numeric base
   * to another, optionally zero-padding to a minimum column width.
   *
   * Supports base 2 through 36; digit values 10-36 are represented
   * as lowercase letters a-z. Input is case-insensitive.
   *
   * @param $input string of digits
   * @param $sourceBase int 2-36
   * @param $destBase int 2-36
   * @param $pad int 1 or greater
   * @param $lowercase bool
   * @return string or false on invalid input
   */
  public static function baseConvert($input, $sourceBase, $destBase, $pad = 1, $lowercase = true)
  {
    $input = strval($input);
    if
    (
      $sourceBase < 2 ||
      $sourceBase > 36 ||
      $destBase < 2 ||
      $destBase > 36 ||
      $pad < 1 ||
      $sourceBase != intval($sourceBase) ||
      $destBase != intval($destBase) ||
      $pad != intval($pad) ||
      !is_string($input) ||
      $input == ''
    )
    {
      return false;
    }
    $digitChars = ( $lowercase ) ?  '0123456789abcdefghijklmnopqrstuvwxyz' : '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $inDigits   = array();
    $outChars   = '';

    //Decode and validate input string
    $input = strtolower( $input );
    for($i = 0; $i < strlen( $input ); $i++)
    {
      $n = strpos($digitChars, $input{$i});
      if($n === false || $n > $sourceBase)
      {
        return false;
      }
      $inDigits[] = $n;
    }

    //Iterate over the input, modulo-ing out an output digit
    //at a time until input is gone.
    while(count($inDigits))
    {
      $work = 0;
      $workDigits = array();

      //Long division...
      foreach($inDigits as $digit)
      {
        $work *= $sourceBase;
        $work += $digit;

        if($work < $destBase)
        {
          // Gonna need to pull another digit.
          if(count($workDigits))
          {
            //Avoid zero-padding; this lets us find
            //the end of the input very easily when
            //length drops to zero.
            $workDigits[] = 0;
          }
        }
        else
        {
          //Finally! Actual division!
          $workDigits[] = intval($work / $destBase);

          //Isn't it annoying that most programming languages
          //don't have a single divide-and-remainder operator,
          //even though the CPU implements it that way?
          $work = $work % $destBase;
        }
      }

      //All that division leaves us with a remainder,
      //which is conveniently our next output digit.
      $outChars .= $digitChars[$work];

      //And we continue!
      $inDigits = $workDigits;
    }

    while(strlen($outChars) < $pad)
    {
      $outChars .= '0';
    }
    return strrev($outChars);
  }
  public static function fitBoxWidth($boxWidth, $boxHeight, $maxHeight)
  {
    $idealWidth = $boxWidth * $maxHeight / $boxHeight;
    $roundedUp  = ceil( $idealWidth );
    return (round($roundedUp * $boxHeight / $boxWidth) > $maxHeight) ? floor($idealWidth) : $roundedUp;
  }
  public static function scaleHeight($srcWidth, $srcHeight, $dstWidth)
  {
    //Exact integer multiply followed by division
    return ($srcWidth == 0) ? 0 : round($srcHeight * $dstWidth / $srcWidth);
  }
  public static function arrayToCGI($array1, $array2 = NULL)
  {
    if (!is_null($array2))
    {
      $array1 = $array1 + $array2;
    }

    $cgi = '';
    foreach ($array1 as $key => $value)
    {
      if ('' !== $value)
      {
        if ('' != $cgi)
        {
          $cgi .= '&';
        }
        if (is_array($value))
        {
          $firstTime = true;
          foreach($value as $v)
          {
            $cgi .= ($firstTime ? '' : '&') . urlencode( $key . '[]' ) . '=' . urlencode( $v );
            $firstTime = false;
          }
        }
        else
        {
          $cgi .= urlencode($key) . '=' . urlencode($value);
        }
      }
    }
    
    return $cgi;
  }
  public static function suppressWarnings($end = false)
  {
    static $suppressCount = 0;
    static $originalLevel = false;

    if ($end)
    {
      if ($suppressCount)
      {
        --$suppressCount;
        if (!$suppressCount)
        {
          error_reporting($originalLevel);
        }
      }
    }
    else
    {
      if (!$suppressCount)
      {
        $originalLevel = error_reporting(E_ALL & ~(E_WARNING | E_NOTICE));
      }
      ++$suppressCount;
    }
  }
  /**
   * Restore error level to previous value
   */
  public static function restoreWarnings()
  {
    self::suppressWarnings(true);
  }
  /**
   * Tries to get the system directory for temporary files.
   * The TMPDIR, TMP, and TEMP environment variables are checked in sequence,
   * and if none are set /tmp is returned as the generic Unix default.
   *
   * NOTE: When possible, use the tempfile() function to create temporary
   * files to avoid race conditions on file creation, etc.
   *
   * @return string
   */
  public static function tempDir()
  {
    foreach (array( 'TMPDIR', 'TMP', 'TEMP') as $var)
    {
      $tmp = getenv($var);
      if ($tmp && file_exists($tmp) && is_dir($tmp) && is_writable($tmp))
      {
        return $tmp;
      }
    }
    # Hope this is Unix of some kind!
    return '/tmp';
  }
  public static function iniGetBool($setting)
  {
    $val = ini_get($setting);
    // 'on' and 'true' can't have whitespace around them, but '1' can.
    return strtolower($val) == 'on'   ||
           strtolower($val) == 'true' || 
           strtolower($val) == 'yes'  ||
           preg_match("/^\s*[+-]?0*[1-9]/", $val); // approx C atoi() function
}
  public static function initShellLocale()
  {
    static $done = false;
    if ($done) return;
    $done = true;
  
    if (!self::iniGetBool('safe_mode'))
    {
      putenv("LC_CTYPE=".ProjectConfiguration::SHELL_LOCALE);
      setlocale(LC_CTYPE, ProjectConfiguration::SHELL_LOCALE);
    }
  }
  public static function isWindows()
  {
    return (substr(php_uname(), 0, 7) == 'Windows') ? true : false;
  }
  public static function escapeShellArg()
  {
    self::initShellLocale();

    $args   = func_get_args();
    $first  = true;
    $retVal = '';
  
    foreach ($args as $arg)
    {
      if (!$first)
      {
        $retVal .= ' ';
      }
      else
      {
        $first = false;
      }

      if (self::isWindows())
      {
        //Escaping for an MSVC-style command line parser
        //Ref: http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html
        //Double the backslashes before any double quotes. Escape the double quotes.
        $tokens = preg_split('/(\\\\*")/', $arg, -1, PREG_SPLIT_DELIM_CAPTURE);
        $arg    = '';
        $delim  = false;
        
        foreach ($tokens as $token)
        {
          $arg   .= $delim ? str_replace('\\', '\\\\', substr($token, 0, -1)) . '\\"' : $token;
          $delim = !$delim;
        }
        //Double the backslashes before the end of the string, because
        //we will soon add a quote
        $m = array();
        if (preg_match('/^(.*?)(\\\\+)$/', $arg, $m))
        {
          $arg = $m[1] . str_replace('\\', '\\\\', $m[2]);
        }

        //Add surrounding quotes
        $retVal .= '"' . $arg . '"';
      }
      else
      {
        $retVal .= escapeshellarg( $arg );
      }
    }
    return $retVal;
  }
  public static function splitExtensions($filename)
  {
    $bits     = explode('.', $filename);
    $basename = array_shift($bits);
    return array($basename, $bits);
  }
  public static function deleteDirectoryContent($pathName)
  {
    foreach(new DirectoryIterator($pathName) as $fileInfo)
    {
      if ($fileInfo->isFile())
      {
        unlink($fileInfo->getPathname());
      }
    }
  }
  public static function unlink($pathName)
  {
  	if (is_dir($pathName))
  	{
      unlink($pathName);
  	}
  }
}